首先看到Buffer
,有寫過NodeJS等後端語言,應該對此比較熟悉,而這邊只先了解一下
它是用來處理跟memory相關的效能議題,而在前端領域上,近來 Canvas、WebGL等開始走紅,也因此效能越來越受重視,
在這類互動應用中會需要與顯卡等系統有大量讀寫及計算,因此如果JS能夠不透過轉譯,直接操作binary data的話,效率肯定大幅提升。
在ES6中納入了包括ArrayBuffer在內的三個操作binary data的接口:
ArrayBuffer
:Buffer,代表一段記憶體區塊,只能讀不能寫,僅能透過 View 操作其內容。TypedArray
:View,儲存固定型別資料的 Array,例如 Uint8Array(8-bit unsigned integer)、Float64Array(64-bit IEEE floating point number)。DataView
:View,不限制型別,可自定義從哪個 byte,以什麼型別,用哪種 byte order(endian)存取。new ArrayBuffer(length)
定義一段固定大小的記憶體區塊,也稱為 byte-array。
主要的功能就是配置實體記憶體來儲存 raw binary data。
如果要操作其內容,僅能透過View
(TypedArray , DataView)。
以下有幾種取得ArrayBuffer的方式:
// 配置16 bytes的記憶體區塊
const buf = new ArrayBuffer(16)
console.log(buf.byteLength) // 16
console.log(buf.slice(0, 2).byteLength) // 2
或是透過FileReader
API 轉換File
物件,得到使用者上傳的檔案數據。
const fileReader = new FileReader()
fileReader.addEventListener('load', e => console.log(e.result))
fileReader.readAsArrayBuffer(blob)
也能像Blob,設定Api response type。
var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "arraybuffer"; //也能設為blob
oReq.onload = function (oEvent) {
var arrayBuffer = oReq.response; // Note: not oReq.responseText
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
// do something with each byte in the array
}
}
};
oReq.send(null);
引用自MDN
TypedArray其實不是一個api或是類別,而是泛指不同限定型別(typed)的陣列,
且都是用來操作binary data。
目前ES6定義了九種typed array types,詳細可以查看MDN,這邊就不一一介紹。
而幾個需要注意的點:
var buf = new ArrayBuffer(64)
var int8 = new Uint8Array(buf) // [0....] byteLength = 64
var int16 = new Uint16Array(buf) // [0...] byteLength = 32
// new Uint32Array(buf) => byteLength = 16
// new Float64Array(buf) => byteLength = 8
int8[0] = 42
console.log(int8) // [42,0....]
console.log(int16) // [42,0....]
pop
/push
...),畢竟是參考ArrayBuffer而其基本就是定義一個固定長度的記憶體區塊,所以只能讀寫內容但無法改變其長度。var buffer = new ArrayBuffer(2) // 2 bytes
var word = new Uint16Array(buffer)
// 'A'.charCodeAt() === 65
// 'B'.charCodeAt() === 66
var value = (65 << 8) + 66 // 使用位元運算子(<< 左移)並填入
word[0] = value
var blob = new Blob([buffer], {type: 'text/plain'})
var dataUri = window.URL.createObjectURL(blob)
window.open(dataUri)
// 轉換出來'BA'
先來看看範例:
var buffer = new ArrayBuffer(2);
new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
// Int16Array uses the platform's endianness.
console.log(new Int16Array(buffer)[0] === 256)
跟TypedArray不同的是,建構時不會固定其資料型別,只有當需要存取時透過getter/setter的方式,指定data type以及從哪個byte offset開始取出資料。
除此之外,另一個重點就是上面setter 函式後面多帶的一個boolean參數及標註的littleEndian
,就是所謂的Endianness,主要會牽扯到與系統層面相關的讀寫效能,這邊就不深入研究。
這邊試試同上TypedArray的範例,看看有什麼不同~
var buffer = new ArrayBuffer(2) // array buffer for two bytes
var view = new DataView(buffer)
// 'A'.charCodeAt() === 65
// 'B'.charCodeAt() === 66
var value = (65 << 8) + 66 // 使用位元運算子(<< 左移)並填入
view.setUint16(0, value)
var blob = new Blob([buffer], {type: 'text/plain'})
var dataUri = window.URL.createObjectURL(blob)
window.open(dataUri)
// 轉換出來'AB'
從結果就能看出其中DataView與TypedArray的Endianness方式不同,
引用網路上資源解釋:
也因為這個差異,可以了解到,DataView因為能夠彈性指定 endian ,其在接收或是發送 Binary data 的應用場景上會比TypedArray安全許多,如通過 XMLHttpRequest, FileReader, 或是 任何 input/output API來交換資料的應用下,相對的,TypedArray因為 endian 預設是根據當下系統CUP的設定,在同個系統下的應用會比較適合,如Canvas或WebGL等與系統有著大量讀寫的互動應用。
本章了解到:
ArrayBuffer
:
TypedArray
:
DataView
:
額外:
如要更深入了解,記憶體操作,以下相關知識就一定要去瞭解看看:
Endianness
Data Structure Alignment
新手入門,如有錯誤,歡迎指正~~~
系列文章同步更新於部落格